package com.flow.framework.common.stream;

import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

/**
 * 安全流，用于防止一行中的字符过多导致内存溢出
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/1/2
 */
public final class SafeBufferedReader extends BufferedReader {
    private static final int DEFAULT_MAX_LINE_LENGTH = 5120;
    private static final int CR = 13;
    private static final int LF = 10;
    private int readerMaxLines = -1;
    private int readerMaxLineLen;
    private int currentLine = 0;

    public SafeBufferedReader(Reader reader, int maxLines, int maxLineLen) {
        super(reader);
        if (maxLines > 0 && maxLineLen > 0) {
            this.readerMaxLines = maxLines;
            this.readerMaxLineLen = maxLineLen;
        } else {
            throw new CheckedException(SystemErrorCode.UNEXPECTED_ERROR, "max lines and max line length must be greater than 0");
        }
    }

    public SafeBufferedReader(Reader reader) {
        super(reader);
        this.readerMaxLineLen = DEFAULT_MAX_LINE_LENGTH;
    }

    @Override
    public String readLine() {
        if (readerMaxLines != -1 && this.currentLine > this.readerMaxLines) {
            throw new CheckedException(SystemErrorCode.UNEXPECTED_ERROR, "max lines can't greater " + this.readerMaxLines + ".");
        } else {
            try {
                ++this.currentLine;
                int currentPos = 0;
                char[] data = new char[this.readerMaxLineLen];

                int currentCharVal;
                while (true) {
                    currentCharVal = super.read();

                    // 13为行结束符，10为换行符，0为空白字符
                    // 检查是否继续读取某一行
                    if (currentCharVal == CR || currentCharVal == LF || currentCharVal < 0) {
                        break;
                    }
                    data[currentPos++] = (char) currentCharVal;
                    if (currentPos >= this.readerMaxLineLen) {
                        throw new CheckedException(SystemErrorCode.UNEXPECTED_ERROR, "max line length can't greater " + this.readerMaxLines + ".");
                    }
                }
                return this.readResult(currentPos, data, currentCharVal);
            } catch (IOException e) {
                throw new CheckedException(SystemErrorCode.UNEXPECTED_ERROR, "read line error.", e);
            }
        }
    }

    private String readResult(int currentPos, char[] data, int currentCharVal) throws IOException {
        if (currentCharVal < 0) {
            return this.getResultWhenEndFile(currentPos, data);
        } else {
            if (currentCharVal == CR) {
                super.mark(1);
                this.dealNewlineChar();
            } else if (currentCharVal != LF) {
                super.mark(1);
                int nextCharVal = super.read();
                if (nextCharVal == CR) {
                    super.mark(1);
                    this.dealNewlineChar();
                } else if (nextCharVal != LF) {
                    super.reset();
                }
            }
            return new String(data, 0, currentPos);
        }
    }

    private void dealNewlineChar() throws IOException {
        if (super.read() != LF) {
            super.reset();
        }
    }

    private String getResultWhenEndFile(int currentPos, char[] data) {
        return currentPos > 0 ? new String(data, 0, currentPos) : null;
    }
}